package lincen.javame.game.tetris;

import lincen.javame.game.tetris.pieces.Tetris;
import lincen.javame.light.util.Tool;

/**
 * 矩形阵列
 * @author 林岑
 * 
 * 此类是俄罗斯方块中的核心组件
 * 
 * 一.具体包含一下组件:
 * 		1.堆
 * 		2.方块
 * 		3.下一个方块
 * 		4.矩形点阵:
 * 			即当前俄罗斯方块的状态,
 * 			他们的状态都用一个布尔状态表示
 * 			具体, 用户可以通过获得主点阵信息和获得下一个点阵的信息来查看当前的状态
 * 				这个信息是一个boolean二维数组
 * 				分别表示当前点阵的开关状态
 * 
 * 二.外部接口:
 * 		1.获得主点阵信息(boolean二维数组表示) √ 
 * 		2.获得主点阵宽度
 * 		3.获得主点阵高度
 * 		2.获得下一个方块的 点阵信息(同样boolean二维数组表示)  √
 * 		3.down()方块下移
 * 		4.left()方块左移
 * 		5.right()方块右移
 * 		6.isDeath()判断玩家是否死亡
 * 		7.deleteLines()删除满行
 * 		8.turnLeft()逆时针旋转
 */
public class Matrix {

	/**
	 * 堆
	 */
	private Stack stack;

	/**
	 * 当前方块
	 */
	private Tetris tNow;

	/**
	 * 下一个方块
	 */
	private Tetris tNext;

	/**
	 * 行列数
	 */
	private int col, row;

	/**
	 * 下一个方块所对应的点阵
	 * 此点阵与tNext始终保持一致
	 */
	private boolean[][] pointNext;

	/**
	 * 画布点阵
	 * 此点阵与tNow和stack时钟保持一致
	 */
	private boolean[][] pointGraphics;

	/**
	 * 构造一个宽度高度为w, h的俄罗斯方块核心组件
	 * @param row
	 * @param col
	 */
	public Matrix(int row, int col) {
		init(row, col);
	}

	/**
	 * 当前方块下沉
	 * 返回结果:
	 * @return 是否落到堆上
	 */
	public synchronized final boolean down() {
		this.tNow.move(0, 1);
		if(
				this.tNow.isCollidesWith(this.stack) || //如果碰到了堆
				isPassTheBaseline(this.tNow) 			//如果超过了底线
		) {
			this.tNow.move(0, -1);

			//进入下一周期

			//将当前方块加入到堆中
			stack.append(this.tNow);

			//将tNext传给tNow, 并居中tNow的横坐标, 纵坐标设置为 -4
			//生成一个随机方块放在tNext中
			setTetrisNext();
			update();
			return true;
		} else {
			update();
			return false;
		}
	}

	/**
	 * 当前方块向左移动
	 * 如果碰壁或者碰到了方块堆
	 * 那么就不执行此操作
	 */
	public synchronized final void left() {
		this.tNow.move(-1, 0);
		if(
				isPassLeftline(this.tNow) ||
				this.tNow.isCollidesWith(this.stack)
		) {
			this.tNow.move(1, 0);
		}
		update();
	}

	/**
	 * 当前方块向右移动
	 * 如果碰壁或者碰到了方块堆
	 * 就不执行此操作
	 */
	public synchronized final void right() {
		this.tNow.move(1, 0);
		if(
				isPassRightline(this.tNow) ||
				this.tNow.isCollidesWith(this.stack)
		) {
			this.tNow.move(-1, 0);
		}
		update();
	}

	/**
	 * 删除满行:
	 * 堆中所有被填满了的行删除掉
	 */
	public synchronized final int clearFullLines() {
		int lineNum = 0;
		for (int y = 0; y < this.getRow(); y++) {
			if(this.stack.deleteLine(y)) {
				lineNum ++;
			}
		}
		update();
		return lineNum;
	}

	/**
	 * 获得下一个方块点阵 对应的boolean二维数组
	 * @return
	 */
	public synchronized final boolean [] [] getPointNext() {
		return this.pointNext;
	}

	/**
	 * 获得主点阵对应的boolean二维数组
	 * @return
	 */
	public synchronized final boolean [] [] getPointGraphics() {
		return this.pointGraphics;
	}

	/**
	 * 当前方块逆时针旋转
	 */
	public synchronized final void turnLeft() {
		this.tNow.turnLeft();
		if (
				isPassLeftline(this.tNow) ||
				isPassRightline(this.tNow) ||
				isPassTheBaseline(this.tNow) ||
				this.tNow.isCollidesWith(this.stack)
		) {
			this.tNow.turnRight();
		}
	}

	/**
	 * 获得主点阵的列数
	 * @return
	 */
	public synchronized final int getCol() {
		return col;
	}

	/**
	 * 主点阵的行数
	 * @return
	 */
	public synchronized final int getRow() {
		return row;
	}

	/**
	 * 判断堆是否满了
	 * @return
	 */
	public synchronized final boolean isStackFull() {

		//遍历堆的第一行元素, 如果第一行中有点存在, 那么就判定堆满
		for (int i = 0; i < this.getCol(); i++) {
			if(this.stack.getPoints()[0][i]) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 返回堆中点数量
	 * @return
	 */
	public int getStackSize() {
		return this.stack.size();
	}

	/**
	 * 复位
	 */
	protected void reset() {
		this.stack = null;
		this.tNow = null;
		this.tNext = null;
		this.stack = new Stack(col, row);
		this.tNow = Tetris.getRandomTetris();
		this.tNext = Tetris.getRandomTetris();
		setTetrisNext();
		update();
	}

	/**
	 * 将准备好的方块传给当前方块,
	 * 同时将当前方块放到屏幕顶端,居中
	 * 然后准备下一个方块
	 */
	private void setTetrisNext() {
		this.tNow = this.tNext;
		toTopAndCenter(this.tNow);
		clearPointNext();
		this.tNext = Tetris.getRandomTetris();
	}

	/**
	 * 清空PointNext中的数据,
	 * 使其全部变为false
	 */
	private void clearPointNext() {
		for (int y = 0; y < this.pointNext.length; y++) {
			for (int x = 0; x < this.pointNext[0].length; x++) {
				this.pointNext[y][x] = false;
			}
		}
	}

	/**
	 * 将当前方块放到屏幕顶端,居中
	 * @param tNow2
	 */
	private void toTopAndCenter(Tetris tNow2) {
		this.tNow.move(0, -4);
		this.tNow.move(this.getCol() / 2 - 2, 0);
	}

	/**
	 * 判断tNow是否超过了左边界
	 * @param tNow
	 * @return
	 */
	private boolean isPassLeftline(Tetris tNow) {
		return tNow.getLeft() < 0;
	}

	/**
	 * 判断tNow是否超过了底线
	 * @param tNow
	 * @return
	 */
	private boolean isPassTheBaseline(Tetris tNow) {
		return tNow.getDown() >= this.getRow();
	}

	/**
	 * 判断tNow是否超过了右边界
	 * @param tNow
	 * @return
	 */
	private boolean isPassRightline(Tetris tNow) {
		return tNow.getRight() >= this.getCol();
	}

	/**
	 * 初始化
	 */
	private void init(int row, int col) {
		this.col = col;
		this.row = row;
		this.pointNext = null;
		this.pointGraphics = null;
		this.pointNext = new boolean [4] [4];
		this.pointGraphics = new boolean [this.row] [this.col];
		reset();
	}

	/**
	 * 将堆/tNow/tNext全部更新至点阵集
	 */
	private void update() {
		updatePointGraphics();
		updatePointNext();
	}

	/**
	 * 更新画布点阵
	 */
	private void updatePointGraphics() {
		updatePointStackToGraphics();
		updatePointNowToGraphics();
	}

	/**
	 * 将堆对应的点阵更新至画布点阵
	 */
	private void updatePointStackToGraphics() {
		boolean [] [] sta = this.stack.getPoints();
		for (int y = 0; y < this.getRow(); y++) {
			for (int x = 0; x < this.getCol(); x++) {
				if(sta[y][x]) {
					this.pointGraphics[y][x] = true;
				} else {
					this.pointGraphics[y][x] = false;
				}
			}
		}
	}

	/**
	 * 更新下一个方块对应点阵
	 */
	private void updatePointNext() {
		Tool.copyBoolean(this.tNext.getPoint(), this.pointNext);
	}

	/**
	 * 将tNow对应的点阵更新到画布点阵中
	 */
	private void updatePointNowToGraphics() {
		boolean [] [] now = this.tNow.getPoint();
		int x1, y1;
		for (int y = 0; y < now.length; y++) {
			for (int x = 0; x < now[y].length; x++) {
				if(now[y][x]) {
					x1 = x + this.tNow.getDx();
					y1 = y + this.tNow.getDy();
					if(
							x1 >= 0 && x1 < this.getCol() &&
							y1 >= 0 && y1 < this.getRow()
					) {
						this.pointGraphics[y1][x1] = true;
					}
				}
			}
		}
	}
}
